package com.cuixichang.minimalism.core.security.intercept;

import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.cuixichang.minimalism.basal.base.Result;
import com.cuixichang.minimalism.basal.cnstants.HttpHeader;
import com.cuixichang.minimalism.basal.enums.ResponseCoderEnum;
import com.cuixichang.minimalism.basal.utils.RegexValidateUtil;
import com.cuixichang.minimalism.core.security.userdetails.CredentialDetail;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Objects;

public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
    private static final Log logfactory = LogFactory.get();
    private FilterInvocationSecurityMetadataSource dynamicSecurityMetadataSource;
    public DynamicSecurityFilter(FilterInvocationSecurityMetadataSource dynamicSecurityMetadataSource,
                                 AccessDecisionManager accessDecisionManager) {
        this.dynamicSecurityMetadataSource = dynamicSecurityMetadataSource;
        super.setAccessDecisionManager(accessDecisionManager);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        if(logfactory.isDebugEnabled()){
            logfactory.debug("DynamicSecurityFilter 执行权限检查");
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
        //OPTIONS请求直接放行
        if (String.valueOf(HttpMethod.OPTIONS).equals(request.getMethod())) {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            return;
        }
        // 请求头前置检查
        String systemCode = request.getHeader(HttpHeader.SYSTEM_SOURCE_CODE);
        if(systemCode==null){
            writeResponse(servletResponse,Result.error(ResponseCoderEnum.SystemUnFind.getCode(),
                    MessageFormat.format(ResponseCoderEnum.SystemUnFind.getDesc(), HttpHeader.SYSTEM_SOURCE_CODE)));
            return;
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication instanceof UsernamePasswordAuthenticationToken){
            CredentialDetail principal = (CredentialDetail) authentication.getPrincipal();
            if(Objects.nonNull(principal)){
                if(!Objects.equals(principal.getSystemCode(), systemCode)){
                    writeResponse(servletResponse,Result.error(ResponseCoderEnum.SystemUnMatched.getCode(),
                            ResponseCoderEnum.SystemUnMatched.getDesc()));
                    return;
                }
            }
        }

        //此处会调用AccessDecisionManager中的decide方法进行鉴权操作
        InterceptorStatusToken  token = super.beforeInvocation(fi);
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    @Override
    public void destroy() {
    }
    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return dynamicSecurityMetadataSource;
    }

    private void writeResponse(ServletResponse response, Result result) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream out = response.getOutputStream();
        out.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));
        out.flush();
    }
}
